package com.ddx.myopengles20;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.util.Log;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by dingdx on 2018/8/8.
 */

public class MyGLRender implements GLSurfaceView.Renderer {

    private Context  mContext;

    public MyGLRender(Context  mContext){
        this.mContext=mContext;
    }

    private final String vertexShaderCode =
                    "uniform mat4 vMatrix;\n" +
                    "attribute vec4 vPosition;\n" +
                    "attribute vec2 vCoordinate;\n" +
                    "varying vec2 aCoordinate;\n" +
                    "void main(){\n" +
                    "    gl_Position=vMatrix*vPosition;\n" +
                    "    aCoordinate=vCoordinate;\n" +
                    "}";

    //    //透视变换与眼睛观察位置的 综合矩阵
    //        glVMatrix = GLES20.glGetUniformLocation(mProgram, "vMatrix");
    //        //获取句柄，用于将内存中的顶点坐标传递给GPU
    //        glVPosition = GLES20.glGetAttribLocation(mProgram, "vPosition");
    //        //获取句柄，用于将内存中的纹理坐标传递给GPU
    //        glVCoordinate = GLES20.glGetAttribLocation(mProgram, "vCoordinate");
    //        glVTexture = GLES20.glGetUniformLocation(mProgram, "vTexture");
    //        glVTexture2 = GLES20.glGetUniformLocation(mProgram, "vTexture2");

    private final String fragmentShaderCode =
                    "precision mediump float;\n" +
                    "uniform sampler2D vTexture;\n" +
                    "uniform sampler2D vTexture2;\n" +
                    "varying vec2 aCoordinate;\n" +
                    "void main(){\n" +
                    "    vec4 textColor1=texture2D(vTexture,aCoordinate);" +
                    "    vec4 textColor2=texture2D(vTexture2,aCoordinate);" +
                    "    gl_FragColor=textColor2;\n" +
                    "}";

    private int mProgram;

    //顶点坐标
    private float[] vertexCoords = {
            0f, 0f,    //左上角
            0f, -0.5f,   //左下角
            0.5f, 0f,     //右上角
            0.5f, -0.5f     //右下角
    };

    //纹理坐标-正放图片，纹理坐标与顶点坐标出现的顺序完全相同，则可以呈现出正放的图片
    private float[] textureCoord = {
            0.0f, 0.0f, //左上、原点
            0.0f, 1.0f, //左下
            1.0f, 0.0f, //右上
            1.0f, 1.0f, //右下
    };

    private int glVPosition;
    private int glVTexture;
    private int glVTexture2;
    private int glVCoordinate;
    private int glVMatrix;

    private int textureId;
    private int textureId2;


    private static int COORDS_PER_VERTEX = 2;
    //顶点个数
    private final int vertexCount = vertexCoords.length / COORDS_PER_VERTEX;
    //顶点之间的偏移量，即每一个顶点所占用的字节大小，每个顶点的坐标有3个float数字，所以为3*4
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个float四个字节



    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        matrixUtils = new MatrixUtils();

        Bitmap textureBmp = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.texture_image_arthur);
        textureId = createTexture(textureBmp);

        textureId2 = createTexture2(textureBmp);

        //rgb=0.4表示背景为灰色
        GLES20.glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
//        GLES20.glClearColor(0,0,0,0f);

        //启用透明色功能
        GLES20.glEnable(GLES20.GL_BLEND);
        //当纹理叠加时，采用叠加色的alpha值作为生效值
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
        //启用2d纹理功能，包含2d采样
        GLES20.glEnable(GLES20.GL_TEXTURE_2D);

        //创建OpenGL绘制程序
        mProgram=createProgram(vertexShaderCode,fragmentShaderCode);

        //GLES20.glGetAttribLocation方法：获取着色器程序中，指定为attribute类型变量的id。
        //GLES20.glGetUniformLocation方法：获取着色器程序中，指定为uniform类型变量的id。
        //透视变换与眼睛观察位置的 综合矩阵
        glVMatrix = GLES20.glGetUniformLocation(mProgram, "vMatrix");
        //获取句柄，用于将内存中的顶点坐标传递给GPU
        glVPosition = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //获取句柄，用于将内存中的纹理坐标传递给GPU
        glVCoordinate = GLES20.glGetAttribLocation(mProgram, "vCoordinate");
        glVTexture = GLES20.glGetUniformLocation(mProgram, "vTexture");
        glVTexture2 = GLES20.glGetUniformLocation(mProgram, "vTexture2");
    }


    private MatrixUtils matrixUtils;

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);

        //计算宽高比
        float ratio = (float) width / height;
        //以下设置保证GL绘制的图像 在不同屏幕设备上的适配
        matrixUtils.frustumM(-ratio, ratio, -1, 1, 3, 7);//设置投影
        matrixUtils.setLookAtM(0, 0, 7.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);//设置相机
    }


    @Override
    public void onDrawFrame(GL10 gl10) {

        //用前面步骤中glClearColor方法设置的颜色值填充整个背景色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);


        drawImage(mProgram, matrixUtils.getResultMatrix());
    }

    private void drawImage(int mProgram, float[] mvpMatrix) {
        //1.使用shader程序
        GLES20.glUseProgram(mProgram);
        //将最终变换矩阵传入shader程序
        GLES20.glUniformMatrix4fv(glVMatrix, 1, false, mvpMatrix, 0);

        //将内存中的顶点坐标数组，转换为字节缓冲区，因为opengl只能接受整块的字节缓冲区的数据
        ByteBuffer bb = ByteBuffer.allocateDirect(vertexCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        FloatBuffer vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(vertexCoords);
        //// 设置缓冲区起始位置
        vertexBuffer.position(0);

        //将内存中的纹理坐标数组，转换为字节缓冲区，因为opengl只能接受整块的字节缓冲区的数据
        ByteBuffer cc = ByteBuffer.allocateDirect(textureCoord.length * 4);
        cc.order(ByteOrder.nativeOrder());
        FloatBuffer textureCoordBuffer = cc.asFloatBuffer();
        textureCoordBuffer.put(textureCoord);
        textureCoordBuffer.position(0);
        //绑定纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);

        //将显卡中的第0号纹理单元 赋值给 纹理句柄
        GLES20.glUniform1i(glVTexture, 0);
        GLES20.glUniform1i(glVTexture2, 1);

        //绘制顶点
        GLES20.glEnableVertexAttribArray(glVPosition);
        // 顶点位置数据传入着色器
        GLES20.glVertexAttribPointer(glVPosition, 2, GLES20.GL_FLOAT, false, vertexStride
                , vertexBuffer);

        //绘制内容
        GLES20.glEnableVertexAttribArray(glVCoordinate);
        GLES20.glVertexAttribPointer(glVCoordinate, 2, GLES20.GL_FLOAT, false,
                vertexStride, textureCoordBuffer);

        //图形绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
    }


    private int createTexture(Bitmap textureBmp) {
        int[] texture = new int[1];
        if (textureBmp != null && !textureBmp.isRecycled()) {
            //在显卡的纹理硬件组上选择当前活跃的纹理单元为：第0号纹理单元，默认为0
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            //从offset=0号纹理单元开始生成n=1个纹理，并将纹理id保存到int[]=texture数组中
            GLES20.glGenTextures(1, texture, 0);
            //将生成的纹理与gpu关联为2d纹理类型，传入纹理id作为参数，每次bing之后，后续操作的纹理都是该纹理
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
            //设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
                    GLES20.GL_NEAREST);
            //设置放大过滤为使用纹理中坐标最接近的若干个颜色，通过加权平均算法得到需要绘制的像素颜色
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
                    GLES20.GL_LINEAR);
            //设置环绕方向S，截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                    GLES20.GL_CLAMP_TO_EDGE);
            //设置环绕方向T，截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                    GLES20.GL_CLAMP_TO_EDGE);

            //给纹理传入图像数据，至此，此纹理相关设置已经结束。后续想使用或者操作这个纹理，只要再glBindTexture这个纹理的id即可
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBmp, 0);

            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

            //返回生成的纹理的句柄
            return texture[0];
        }
        return 0;
    }

    private int createTexture2(Bitmap textureBmp) {
        int[] texture = new int[1];
        if (textureBmp != null && !textureBmp.isRecycled()) {

            //和上面的唯一区别
            //在显卡的纹理硬件组上选择当前活跃的纹理单元为：第0号纹理单元，默认为0
            GLES20.glActiveTexture(GLES20.GL_TEXTURE1);



            //从offset=0号纹理单元开始生成n=1个纹理，并将纹理id保存到int[]=texture数组中
            GLES20.glGenTextures(1, texture, 0);
            //将生成的纹理与gpu关联为2d纹理类型，传入纹理id作为参数，每次bing之后，后续操作的纹理都是该纹理
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
            //设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
                    GLES20.GL_NEAREST);
            //设置放大过滤为使用纹理中坐标最接近的若干个颜色，通过加权平均算法得到需要绘制的像素颜色
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
                    GLES20.GL_LINEAR);
            //设置环绕方向S，截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                    GLES20.GL_CLAMP_TO_EDGE);
            //设置环绕方向T，截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                    GLES20.GL_CLAMP_TO_EDGE);

            //给纹理传入图像数据，至此，此纹理相关设置已经结束。后续想使用或者操作这个纹理，只要再glBindTexture这个纹理的id即可
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBmp, 0);

            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

            //返回生成的纹理的句柄
            return texture[0];
        }
        return 0;
    }


    //创建OpenGL绘制程序
    private int createProgram(String vertexSource, String fragmentSource) {
        int vertex = getShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertex == 0) {
            return vertex;
        }
        int fragment = getShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (fragment == 0) {
            return fragment;
        }
        int program = GLES20.glCreateProgram();//创建程序  必要
        if (program != 0) {
            GLES20.glAttachShader(program, vertex);//附加着色器 必要
            GLES20.glAttachShader(program, fragment);
            GLES20.glLinkProgram(program);//链接程序 必要
            int[] status = new int[1];
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, status, 0);
            if (status[0] == 0) {
                GLES20.glDeleteProgram(program);
            }
        }
        return program;
    }

    //获取着色器  编译多边形着色器代码
    private int getShader(int shaderType, String sourceCode) {
        int shader = GLES20.glCreateShader(shaderType); //创建着色器 必要
        if (shader != 0) {
            //着色器为已编译的二进制数据时可省以下步骤
            GLES20.glShaderSource(shader, sourceCode); //添加着色器代码 必要
            GLES20.glCompileShader(shader); //编译 必要
            int[] status = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0);
            if (status[0] == 0) {
                GLES20.glDeleteShader(shader);
                return 0;
            }
        }
        return shader;
    }

}
